<?php

namespace App\Services\Impl;

use App\Constants\OppoEventTypeEnum;
use App\Constants\SystemEventTypeEnum;
use App\Entity\Bo\ReportEventBo;
use App\Model\Business\Application;
use App\Model\Report\BigDevice;
use App\Model\Report\OppoDevice;
use App\Model\Report\OppoDeviceEvent;
use App\Model\Report\ReportThirdLog;
use App\Utils\OppoUtil;
use Carbon\Carbon;
use Hyperf\DbConnection\Db;

class OppoService
{
    public array $systemEventTypeToOppoEventTypeMap = [];

    public function __construct()
    {
        $this->systemEventTypeToOppoEventTypeMap = [
            SystemEventTypeEnum::ACTIVATION->value => OppoEventTypeEnum::ACTIVATION->value,
            SystemEventTypeEnum::NEXT_DAY->value   => OppoEventTypeEnum::NEXT_DAY->value,
            SystemEventTypeEnum::KEY->value        => OppoEventTypeEnum::REGISTER->value,
        ];
    }

    public function reportLaunch($appId, $oaid)
    {
        $device = OppoDevice::findReportAscribeDevice($appId, $oaid);

        if ($device) {
            if ($device->launch_status != 1) {
                $device->launch_status = 1;
                $device->launch_time   = Carbon::now()->toDateTimeString();
                $device->save();
            }
        }
    }

    public function reportEvent(ReportEventBo $reportEventBo)
    {
        $app             = $reportEventBo->getApp();
        $appId           = $reportEventBo->getAppId();
        $systemEventType = $reportEventBo->getEventType();
        $oaid            = $reportEventBo->getOaid();

        $eventType = $this->systemEventTypeToOppoEventTypeMap[$systemEventType];
        throw_error_if(!$eventType, "oppoEventType 不正确");

        // 32位的oaid不用回传，只回传64位的，32位的媒体侧识别不了
        throw_error_if(strlen($oaid) != 64, "无效oaid");

        $device = $this->getReportDevice($reportEventBo);
        throw_error_if(!$device->ad_id, "没有广告计划，不上报");

        $this->reportApi($reportEventBo, $device, $eventType);
    }

    private function reportApi(ReportEventBo $reportEventBo, OppoDevice $device, $eventType): void
    {
        $projectId          = $reportEventBo->getProjectId();
        $app                = $reportEventBo->getApp();
        $appId              = $reportEventBo->getAppId();
        $channel            = $reportEventBo->getChannel();
        $version            = $reportEventBo->getVersion();
        $oaid               = $reportEventBo->getOaid();
        $bigDevice          = $reportEventBo->getBigDevice();
        $systemEventType    = $reportEventBo->getEventType();
        $packageName        = $app->package_name;
        $ascribeChannelType = $bigDevice->channel_type;
        $ascribeChannel     = $bigDevice->channel;

        $keyEventTypeAry = $this->getReportKeyEventTypeAry($bigDevice);

        // 校验是否有重复上报的，如果是关键行为，还有可以上报的事件类型，则会返回还有哪些没上报的关键行为类型
        $keyEventTypeAry = $this->checkRepeatReport($systemEventType, $eventType, $device, $keyEventTypeAry);

        Db::beginTransaction();
        try {

            $thirdpartyDeviceUpdateData = [];

            // 如果是关键行为
            if ($systemEventType == SystemEventTypeEnum::KEY->value) {

                foreach ($keyEventTypeAry as $key => $item) {

                    $reportTimeColumn   = OppoUtil::getReportTimeColumnByEventType($item);
                    $reportStatusColumn = OppoUtil::getReportStatusColumnByEventType($item);

                    $thirdpartyDeviceUpdateData[$reportStatusColumn] = 1;
                    $thirdpartyDeviceUpdateData[$reportTimeColumn]   = date("Y-m-d H:i:s");

                    OppoDeviceEvent::create([
                        "project_id" => $projectId,
                        'app_id'     => $appId,
                        'channel'    => $channel,
                        'version'    => $version,
                        "oaid"       => $oaid,
                        'event_type' => $item,
                    ])->save();
                }
            } else {
                $reportStatusColumn = OppoUtil::getReportStatusColumnByEventType($eventType);
                $reportTimeColumn   = OppoUtil::getReportTimeColumnByEventType($eventType);

                $thirdpartyDeviceUpdateData[$reportStatusColumn] = 1;
                $thirdpartyDeviceUpdateData[$reportTimeColumn]   = date("Y-m-d H:i:s");

                OppoDeviceEvent::create([
                    "project_id" => $projectId,
                    'app_id'     => $appId,
                    'channel'    => $channel,
                    'version'    => $version,
                    "oaid"       => $oaid,
                    'event_type' => $eventType,
                ])->save();
            }

            // 更新上报状态
            OppoDevice::where("id", $device->id)
                ->update($thirdpartyDeviceUpdateData);

            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            throw_error("数据库错误: " . $e->getMessage());
        }

        if ($systemEventType == SystemEventTypeEnum::KEY->value) {
            foreach ($keyEventTypeAry as $item) {
                (new OppoUtil())->reportEvent($ascribeChannel, $version, $oaid, $item, $packageName, $device, 1);
            }
        } else {
            (new OppoUtil())->reportEvent($ascribeChannel, $version, $oaid, $eventType, $packageName, $device, 1);
        }

        (new ReportService())->behaviorReportLogic($projectId, $appId, $channel, $version, $ascribeChannelType,
            $ascribeChannel, $oaid, $systemEventType);
    }

    private function checkRepeatReport($systemEventType, $eventType, OppoDevice $device, $keyEventTypeAry): array
    {
        // 已上报过的事件
        $reportedEventTypeAry = ReportThirdLog::query()
            ->where("app_id", $device->app_id)
            ->where("oaid", $device->oaid)
            ->where("ad_plan_id", $device->ad_plan_id)
            ->pluck("event_type")
            ->toArray();

        // 如果上报关键行为
        if ($systemEventType == SystemEventTypeEnum::KEY->value) {

            foreach ($keyEventTypeAry as $key => $item) {
                // 如果当前事件类型不在枚举中，则销毁
                if (!OppoEventTypeEnum::inValue($item)) {
                    unset($keyEventTypeAry[$key]);
                }
            }

            // 判断批量上报的状态中有重复上报的销毁
            foreach ($keyEventTypeAry as $key => $item) {
                $reportStatusColumn = OppoUtil::getReportStatusColumnByEventType($item);
                if ($device->{$reportStatusColumn} != 0 && (in_array($item, $reportedEventTypeAry))) {
                    var_dump("oaid: $device->oaid, $item 重复上报，跳过");
                    unset($keyEventTypeAry[$key]);
                }
            }

            throw_error_if(!$keyEventTypeAry, "关键行为批量上报中全部重复上报");

            return $keyEventTypeAry;
        }

        $reportStatusColumn = OppoUtil::getReportStatusColumnByEventType($eventType);
        throw_error_if($device->{$reportStatusColumn} != 0 && (in_array($eventType, $reportedEventTypeAry)), "重复上报");

        return [];
    }

    private function getReportKeyEventTypeAry(BigDevice $bigDevice)
    {
        if (empty($bigDevice->report_config)) {
            return [];
        }

        $reportConfigAry = $bigDevice->report_config;
        $keyEventTypeAry = $reportConfigAry['key_ary'] ?? [];

        return $keyEventTypeAry;
    }

    public function getReportDevice(ReportEventBo $reportEventBo)
    {
        $appId     = $reportEventBo->getAppId();
        $bigDevice = $reportEventBo->getBigDevice();
        $oaid      = $reportEventBo->getOaid();

        $device = OppoDevice::findReportAscribeDevice($appId, $oaid);
        throw_error_if(!$device, "没有设备信息");

        if ($bigDevice) {
            $bigDevice->sub_device_id = $device->id;
            $bigDevice->save();
        }

        return $device;
    }
}